<?php
/**
 * Created by PhpStorm.
 * Author: Wudean
 * Date: 2019/1/3 0003
 * Time: 14:12
 */
class XingeApp{

    const DEVICE_ALL = 0;
    const DEVICE_BROWSER = 1;
    const DEVICE_PC = 2;
    const DEVICE_ANDROID = 3;
    const DEVICE_IOS = 4;
    const DEVICE_WINPHONE = 5;

    const IOSENV_PROD = 'product';
    const IOSENV_DEV = 'dev';

    const IOS_MIN_ID = 2200000000;

    public function __construct($appId='', $secretKey='', $accessId = '') {
        assert(isset($appId) && isset($secretKey));

        $this->appId = $appId;
        $this->secretKey = $secretKey;
        $this->accessId = $accessId;
    }

    public function __destruct() {
    }

    /**
     * 使用默认设置推送消息给单个android设备
     */
    public static function PushTokenAndroid($appId, $secretKey, $title, $content, $token) {
        $push = new XingeApp($appId, $secretKey);
        $mess = new Message();
        $mess->setTitle($title);
        $mess->setContent($content);
        $mess->setType(Message::TYPE_NOTIFICATION);
        $mess->setStyle(new Style(0, 1, 1, 1, 0));
        $action = new ClickAction();
        $action->setActionType(ClickAction::TYPE_ACTIVITY);
        $mess->setAction($action);
        $ret = $push->PushSingleDevice($token, $mess);
        return $ret;
    }

    /**
     * 使用默认设置推送消息给单个ios设备
     */
    public static function PushTokenIos($appId, $secretKey, $content, $token, $environment) {
        $push = new XingeApp($appId, $secretKey);
        $mess = new MessageIOS();
        $mess->setAlert($content);
        $ret = $push->PushSingleDevice($token, $mess, $environment);
        return $ret;
    }

    /**
     * 使用默认设置推送消息给单个android版账户
     */
    public static function PushAccountAndroid($appId, $secretKey, $title, $content, $account) {
        $push = new XingeApp($appId, $secretKey);
        $mess = new Message();
        $mess->setTitle($title);
        $mess->setContent($content);
        $mess->setType(Message::TYPE_NOTIFICATION);
        $mess->setStyle(new Style(0, 1, 1, 1, 0));
        $action = new ClickAction();
        $action->setActionType(ClickAction::TYPE_ACTIVITY);
        $mess->setAction($action);
        $ret = $push->PushSingleAccount(0, $account, $mess);
        return $ret;
    }

    /**
     * 使用默认设置推送消息给单个ios版账户
     */
    public static function PushAccountIos($appId, $secretKey, $content, $account, $environment) {
        $push = new XingeApp($appId, $secretKey);
        $mess = new MessageIOS();
        $mess->setAlert($content);
        $ret = $push->PushSingleAccount(0, $account, $mess, $environment);
        return $ret;
    }

    /**
     * 使用默认设置推送消息给所有设备android版
     */
    public static function PushAllAndroid($appId, $secretKey, $title, $content) {
        $push = new XingeApp($appId, $secretKey);
        $mess = new Message();
        $mess->setTitle($title);
        $mess->setContent($content);
        $mess->setType(Message::TYPE_NOTIFICATION);
        $mess->setStyle(new Style(0, 1, 1, 1, 0));
        $action = new ClickAction();
        $action->setActionType(ClickAction::TYPE_ACTIVITY);
        $mess->setAction($action);
        $ret = $push->PushAllDevices($mess);
        return $ret;
    }

    /**
     * 使用默认设置推送消息给所有设备ios版
     */
    public static function PushAllIos($appId, $secretKey, $content, $environment) {
        $push = new XingeApp($appId, $secretKey);
        $mess = new MessageIOS();
        $mess->setAlert($content);
        $ret = $push->PushAllDevices($mess, $environment);
        return $ret;
    }

    /**
     * 使用默认设置推送消息给标签选中设备android版
     */
    public static function PushTagAndroid($appId, $secretKey, $title, $content, $tag) {
        $push = new XingeApp($appId, $secretKey);
        $mess = new Message();
        $mess->setTitle($title);
        $mess->setContent($content);
        $mess->setType(Message::TYPE_NOTIFICATION);
        $mess->setStyle(new Style(0, 1, 1, 1, 0));
        $action = new ClickAction();
        $action->setActionType(ClickAction::TYPE_ACTIVITY);
        $mess->setAction($action);
        $ret = $push->PushTags(array($tag), 'OR', $mess);
        return $ret;
    }

    /**
     * 使用默认设置推送消息给标签选中设备ios版
     */
    public static function PushTagIos($appId, $secretKey, $content, $tag, $environment) {
        $push = new XingeApp($appId, $secretKey);
        $mess = new MessageIOS();
        $mess->setAlert($content);
        $ret = $push->PushTags(array($tag), 'OR', $mess, $environment);
        return $ret;
    }

    /**
     * 推送消息给单个设备
     */
    public function PushSingleDevice($deviceToken, $message, $environment = XingeApp::IOSENV_DEV) {
        $ret = array('ret_code' => -1, 'err_msg' => 'message not valid');

        if (!($message instanceof Message) && !($message instanceof MessageIOS)) {
            return $ret;
        }

        if ($message instanceof MessageIOS) {
            if ($environment != XingeApp::IOSENV_DEV && $environment != XingeApp::IOSENV_PROD) {
                $ret['err_msg'] = "ios message environment invalid";
                return $ret;
            }
        }
        // if (!$message->isValid()) return $ret;
        $params = array();
        $params['audience_type'] = 'token';
        $params['token_list'] = array($deviceToken);
        $params['expire_time'] = $message->getExpireTime();
        $params['send_time'] = $message->getSendTime();
        if ($message instanceof Message) {
            $params['platform'] = 'android'; //android：安卓, ios：苹果, all：安卓&&苹果，仅支持全量推送和标签推送
            $params['multi_pkg'] = $message->getMultiPkg();
        }
        if ($message instanceof MessageIOS) {
            $params['platform'] = 'ios';
            $params['environment'] = $environment;
        }
        $params['message_type'] = $message->getType();
        $params['message'] = $message->toJson();

        $params['timestamp'] = time();
        $params['seq'] = time();

        return $this->callRestful(self::RESTAPI_PUSH, $params);
    }

    /**
     * 推送消息给单个账户
     */
    public function PushSingleAccount($account, $message, $environment = XingeApp::IOSENV_DEV) {
        $ret = array('ret_code' => -1);
        if (!is_string($account) || empty($account)) {
            $ret['err_msg'] = 'account not valid';
            return $ret;
        }
        if (!($message instanceof Message) && !($message instanceof MessageIOS)) {
            $ret['err_msg'] = 'message is not android or ios';
            return $ret;
        }
        if ($message instanceof MessageIOS) {
            if ($environment != XingeApp::IOSENV_DEV && $environment != XingeApp::IOSENV_PROD) {
                $ret['err_msg'] = "ios message environment invalid";
                return $ret;
            }
        }
        $params = array();
        $params['audience_type'] = 'account';
        $params['account_list'] = array($account);
        $params['expire_time'] = $message->getExpireTime();
        $params['send_time'] = $message->getSendTime();
        if ($message instanceof Message) {
            $params['platform'] = 'android'; //android：安卓, ios：苹果, all：安卓&&苹果，仅支持全量推送和标签推送
            $params['multi_pkg'] = $message->getMultiPkg();
        }
        if ($message instanceof MessageIOS) {
            $params['platform'] = 'ios';
            $params['environment'] = $environment;
        }
        //dd($params);
        $params['message_type'] = $message->getType();
        $params['message'] = $message->toJson();
        $params['timestamp'] = time();
        $params['seq'] = time();
        return $this->callRestful(self::RESTAPI_PUSH, $params);
    }

    /**
     * 推送消息给多个账户
     */
    public function PushTokenList($tokenList, $message, $environment = XingeApp::IOSENV_DEV) {
        $ret = array('ret_code' => -1);
        if (!is_array($tokenList) || empty($tokenList)) {
            $ret['err_msg'] = 'tokenList not valid';
            return $ret;
        }
        if (!($message instanceof Message) && !($message instanceof MessageIOS)) {
            $ret['err_msg'] = 'message is not android or ios';
            return $ret;
        }
        if ($message instanceof MessageIOS) {
            if ($environment != XingeApp::IOSENV_DEV && $environment != XingeApp::IOSENV_PROD) {
                $ret['err_msg'] = "ios message environment invalid";
                return $ret;
            }
        }
        $params = array();
        $params['audience_type'] = 'token_list';
        $params['token_list'] = $tokenList;
        $params['expire_time'] = $message->getExpireTime();
        $params['send_time'] = $message->getSendTime();
        if ($message instanceof Message) {
            $params['platform'] = 'android'; //android：安卓, ios：苹果, all：安卓&&苹果，仅支持全量推送和标签推送
            $params['multi_pkg'] = $message->getMultiPkg();
        }
        if ($message instanceof MessageIOS) {
            $params['platform'] = 'ios';
            $params['environment'] = $environment;
        }
        $params['message_type'] = $message->getType();
        $params['message'] = $message->toJson();
        $params['timestamp'] = time();
        $params['seq'] = time();

        return $this->callRestful(self::RESTAPI_PUSH, $params);
    }

    /**
     * 推送消息给多个账户
     */
    public function PushAccountList($accountList, $message, $environment = XingeApp::IOSENV_DEV) {
        $ret = array('ret_code' => -1);
        if (!is_array($accountList) || empty($accountList)) {
            $ret['err_msg'] = 'accountList not valid';
            return $ret;
        }
        if (!($message instanceof Message) && !($message instanceof MessageIOS)) {
            $ret['err_msg'] = 'message is not android or ios';
            return $ret;
        }
        if ($message instanceof MessageIOS) {
            if ($environment != XingeApp::IOSENV_DEV && $environment != XingeApp::IOSENV_PROD) {
                $ret['err_msg'] = "ios message environment invalid";
                return $ret;
            }
        }
        $params = array();
        $params['audience_type'] = 'account';
        $params['account_list'] = $accountList;
        $params['expire_time'] = $message->getExpireTime();
        $params['send_time'] = $message->getSendTime();
        if ($message instanceof Message) {
            $params['platform'] = 'android'; //android：安卓, ios：苹果, all：安卓&&苹果，仅支持全量推送和标签推送
            $params['multi_pkg'] = $message->getMultiPkg();
        }
        if ($message instanceof MessageIOS) {
            $params['platform'] = 'ios';
            $params['environment'] = $environment;
        }
        $params['message_type'] = $message->getType();
        $params['message'] = $message->toJson();
        $params['timestamp'] = time();
        $params['seq'] = time();

        return $this->callRestful(self::RESTAPI_PUSH, $params);
    }

    /**
     * 推送消息给APP所有设备
     */
    public function PushAllDevices($message, $environment = XingeApp::IOSENV_DEV) {
        $ret = array('ret_code' => -1, 'err_msg' => 'message not valid');

        if (!($message instanceof Message) && !($message instanceof MessageIOS)) {
            return $ret;
        }

        if ($message instanceof MessageIOS) {
            if ($environment != XingeApp::IOSENV_DEV && $environment != XingeApp::IOSENV_PROD) {
                $ret['err_msg'] = "ios message environment invalid";
                return $ret;
            }
        }
        $params = array();
        $params['audience_type'] = 'all';
        $params['expire_time'] = $message->getExpireTime();
        $params['send_time'] = $message->getSendTime();
        if ($message instanceof Message) {
            $params['platform'] = 'android'; //android：安卓, ios：苹果, all：安卓&&苹果，仅支持全量推送和标签推送
            $params['multi_pkg'] = $message->getMultiPkg();
        }
        if ($message instanceof MessageIOS) {
            $params['platform'] = 'ios';
            $params['environment'] = $environment;
        }
        $params['message_type'] = $message->getType();
        $params['message'] = $message->toJson();
        $params['timestamp'] = time();
        $params['seq'] = time();

        if (!is_null($message->getLoopInterval()) && $message->getLoopInterval() > 0
            && !is_null($message->getLoopTimes()) && $message->getLoopTimes() > 0
        ) {
            $params['loop_interval'] = $message->getLoopInterval();
            $params['loop_times'] = $message->getLoopTimes();
        }

        return $this->callRestful(self::RESTAPI_PUSH, $params);
    }

    /**
     * 推送消息给指定tags的设备
     * 若要推送的tagList只有一项，则tagsOp应为OR
     */
    public function pushTags($tagList, $tagsOp, $message, $environment = XingeApp::IOSENV_DEV) {
        $ret = array('ret_code' => -1, 'err_msg' => 'message not valid');
        if (!is_array($tagList) || empty($tagList)) {
            $ret['err_msg'] = 'tagList not valid';
            return $ret;
        }
        if (!is_string($tagsOp) || ($tagsOp != 'AND' && $tagsOp != 'OR')) {
            $ret['err_msg'] = 'tagsOp not valid';
            return $ret;
        }

        if (!($message instanceof Message) && !($message instanceof MessageIOS)) {
            return $ret;
        }

        if ($message instanceof MessageIOS) {
            if ($environment != XingeApp::IOSENV_DEV && $environment != XingeApp::IOSENV_PROD) {
                $ret['err_msg'] = "ios message environment invalid";
                return $ret;
            }
        }

        $params = array();
        $params['audience_type'] = 'tag';
        $params['tag_list'] = array(
            'tags' => $tagList,
            'op' => $tagsOp,
        );
        $params['expire_time'] = $message->getExpireTime();
        $params['send_time'] = $message->getSendTime();
        if ($message instanceof Message) {
            $params['platform'] = 'android'; //android：安卓, ios：苹果, all：安卓&&苹果，仅支持全量推送和标签推送
            $params['multi_pkg'] = $message->getMultiPkg();
        }
        if ($message instanceof MessageIOS) {
            $params['platform'] = 'ios';
            $params['environment'] = $environment;
        }
        $params['message_type'] = $message->getType();
        $params['message'] = $message->toJson();
        $params['timestamp'] = time();
        $params['seq'] = time();

        if (!is_null($message->getLoopInterval()) && $message->getLoopInterval() > 0
            && !is_null($message->getLoopTimes()) && $message->getLoopTimes() > 0
        ) {
            $params['loop_interval'] = $message->getLoopInterval();
            $params['loop_times'] = $message->getLoopTimes();
        }

        return $this->callRestful(self::RESTAPI_PUSH, $params);
    }

    /**
     * 查询消息推送状态
     * @param array $pushIdList pushId(string)数组
     */
    public function QueryPushStatus($pushIdList) {
        $ret = array('ret_code' => -1);
        $idList = array();
        if (!is_array($pushIdList) || empty($pushIdList)) {
            $ret['err_msg'] = 'pushIdList not valid';
            return $ret;
        }
        foreach ($pushIdList as $pushId) {
            $idList[] = array('push_id' => $pushId);
        }
        $params = array();
        $params['access_id'] = $this->accessId;
        $params['push_ids'] = json_encode($idList);
        $params['timestamp'] = time();

        return $this->callRestfulForOld(self::RESTAPI_QUERYPUSHSTATUS, $params);
    }

    /**
     * 查询应用覆盖的设备数
     */
    public function QueryDeviceCount() {
        $params = array();
        $params['access_id'] = $this->accessId;
        $params['timestamp'] = time();

        return $this->callRestfulForOld(self::RESTAPI_QUERYDEVICECOUNT, $params);
    }

    /**
     * 查询应用标签
     */
    public function QueryTags($start = 0, $limit = 100) {
        $ret = array('ret_code' => -1);
        if (!is_int($start) || !is_int($limit)) {
            $ret['err_msg'] = 'start or limit not valid';
            return $ret;
        }
        $params = array();
        $params['access_id'] = $this->accessId;
        $params['start'] = $start;
        $params['limit'] = $limit;
        $params['timestamp'] = time();

        return $this->callRestfulForOld(self::RESTAPI_QUERYTAGS, $params);
    }

    /**
     * 查询标签下token数量
     */
    public function QueryTagTokenNum($tag) {
        $ret = array('ret_code' => -1);
        if (!is_string($tag)) {
            $ret['err_msg'] = 'tag is not valid';
            return $ret;
        }
        $params = array();
        $params['access_id'] = $this->accessId;
        $params['tag'] = $tag;
        $params['timestamp'] = time();

        return $this->callRestfulForOld(self::RESTAPI_QUERYTAGTOKENNUM, $params);
    }

    /**
     * 查询token的标签
     */
    public function QueryTokenTags($deviceToken) {
        $ret = array('ret_code' => -1);
        if (!is_string($deviceToken)) {
            $ret['err_msg'] = 'deviceToken is not valid';
            return $ret;
        }
        $params = array();
        $params['access_id'] = $this->accessId;
        $params['device_token'] = $deviceToken;
        $params['timestamp'] = time();

        return $this->callRestfulForOld(self::RESTAPI_QUERYTOKENTAGS, $params);
    }

    /**
     * 取消定时发送
     */
    public function CancelTimingPush($pushId) {
        $ret = array('ret_code' => -1);
        if (!is_string($pushId) || empty($pushId)) {
            $ret['err_msg'] = 'pushId not valid';
            return $ret;
        }
        $params = array();
        $params['access_id'] = $this->accessId;
        $params['push_id'] = $pushId;
        $params['timestamp'] = time();

        return $this->callRestfulForOld(self::RESTAPI_CANCELTIMINGPUSH, $params);
    }

    //json转换为数组
    protected function json2Array($json) {
        $json = stripslashes($json);
        return json_decode($json, true);
    }

    protected function callRestful($url, $params) {
        $paramsBase = new ParamsBase($params);
        $extra_curl_conf = array(
            CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
            CURLOPT_USERPWD => $this->appId . ':' . $this->secretKey,
        );

        $requestBase = new RequestBase();
        $ret = $this->json2Array($requestBase->exec(
            $url,
            $params,
            RequestBase::METHOD_POST,
            $extra_curl_conf
        ));

        return $ret;
    }

    protected function callRestfulForOld($url, $params) {
        $paramsBase = new ParamsBase($params);
        $sign = $paramsBase->generateSign(RequestBase::METHOD_POST, $url, $this->secretKey);
        $params['sign'] = $sign;
        $requestBase = new RequestBase();
        $ret = $this->json2Array($requestBase->execForOld(
            $url,
            $params,
            RequestBase::METHOD_POST
        ));

        return $ret;
    }

    private function ValidateToken($token) {
        if ($this->accessId >= 2200000000) {
            return strlen($token) == 64;
        } else {
            return (strlen($token) == 40 || strlen($token) == 64);
        }
    }

    public function InitParams() {

        $params = array();
        $params['access_id'] = $this->accessId;
        $params['timestamp'] = time();

        return $params;
    }

    public function BatchSetTag($tagTokenPairs) {
        $ret = array('ret_code' => -1);

        foreach ($tagTokenPairs as $pair) {
            if (!($pair instanceof TagTokenPair)) {
                $ret['err_msg'] = 'tag-token pair type error!';
                return $ret;
            }
            if (!$this->ValidateToken($pair->token)) {
                $ret['err_msg'] = sprintf("invalid token %s", $pair->token);
                return $ret;
            }
        }
        $params = $this->InitParams();

        $tag_token_list = array();
        foreach ($tagTokenPairs as $pair) {
            array_push($tag_token_list, array($pair->tag, $pair->token));
        }
        $params['tag_token_list'] = json_encode($tag_token_list);

        return $this->callRestfulForOld(self::RESTAPI_BATCHSETTAG, $params);
    }

    public function BatchDelTag($tagTokenPairs) {
        $ret = array('ret_code' => -1);

        foreach ($tagTokenPairs as $pair) {
            if (!($pair instanceof TagTokenPair)) {
                $ret['err_msg'] = 'tag-token pair type error!';
                return $ret;
            }
            if (!$this->ValidateToken($pair->token)) {
                $ret['err_msg'] = sprintf("invalid token %s", $pair->token);
                return $ret;
            }
        }
        $params = $this->InitParams();

        $tag_token_list = array();
        foreach ($tagTokenPairs as $pair) {
            array_push($tag_token_list, array($pair->tag, $pair->token));
        }
        $params['tag_token_list'] = json_encode($tag_token_list);

        return $this->callRestfulForOld(self::RESTAPI_BATCHDELTAG, $params);
    }

    public function QueryInfoOfToken($deviceToken) {
        $ret = array('ret_code' => -1);
        if (!is_string($deviceToken)) {
            $ret['err_msg'] = 'deviceToken is not valid';
            return $ret;
        }
        $params = array();
        $params['access_id'] = $this->accessId;
        $params['device_token'] = $deviceToken;
        $params['timestamp'] = time();

        return $this->callRestfulForOld(self::RESTAPI_QUERYINFOOFTOKEN, $params);
    }

    public function QueryTokensOfAccount($account) {
        $ret = array('ret_code' => -1);
        if (!is_string($account)) {
            $ret['err_msg'] = 'account is not valid';
            return $ret;
        }
        $params = array();
        $params['access_id'] = $this->accessId;
        $params['account'] = $account;
        $params['timestamp'] = time();

        return $this->callRestfulForOld(self::RESTAPI_QUERYTOKENSOFACCOUNT, $params);
    }

    public function DeleteTokenOfAccount($account, $deviceToken) {
        $ret = array('ret_code' => -1);
        if (!is_string($account) || !is_string($deviceToken)) {
            $ret['err_msg'] = 'account or deviceToken is not valid';
            return $ret;
        }
        $params = array();
        $params['access_id'] = $this->accessId;
        $params['account'] = $account;
        $params['device_token'] = $deviceToken;
        $params['timestamp'] = time();

        return $this->callRestfulForOld(self::RESTAPI_DELETETOKENOFACCOUNT, $params);
    }

    public function DeleteAllTokensOfAccount($account) {
        $ret = array('ret_code' => -1);
        if (!is_string($account)) {
            $ret['err_msg'] = 'account is not valid';
            return $ret;
        }
        $params = array();
        $params['access_id'] = $this->accessId;
        $params['account'] = $account;
        $params['timestamp'] = time();

        return $this->callRestfulForOld(self::RESTAPI_DELETEALLTOKENSOFACCOUNT, $params);
    }

    public $appId = ''; //应用的接入Id
    public $secretKey = ''; //应用的skey
    public $accessId = ''; //应用的skey

    const RESTAPI_QUERYPUSHSTATUS = 'http://openapi.xg.qq.com/v2/push/get_msg_status';
    const RESTAPI_QUERYDEVICECOUNT = 'http://openapi.xg.qq.com/v2/application/get_app_device_num';
    const RESTAPI_QUERYTAGS = 'http://openapi.xg.qq.com/v2/tags/query_app_tags';
    const RESTAPI_CANCELTIMINGPUSH = 'http://openapi.xg.qq.com/v2/push/cancel_timing_task';
    const RESTAPI_BATCHSETTAG = 'http://openapi.xg.qq.com/v2/tags/batch_set';
    const RESTAPI_BATCHDELTAG = 'http://openapi.xg.qq.com/v2/tags/batch_del';
    const RESTAPI_QUERYTOKENTAGS = 'http://openapi.xg.qq.com/v2/tags/query_token_tags';
    const RESTAPI_QUERYTAGTOKENNUM = 'http://openapi.xg.qq.com/v2/tags/query_tag_token_num';
    const RESTAPI_QUERYINFOOFTOKEN = 'http://openapi.xg.qq.com/v2/application/get_app_token_info';
    const RESTAPI_QUERYTOKENSOFACCOUNT = 'http://openapi.xg.qq.com/v2/application/get_app_account_tokens';
    const RESTAPI_DELETETOKENOFACCOUNT = 'http://openapi.xg.qq.com/v2/application/del_app_account_tokens';
    const RESTAPI_DELETEALLTOKENSOFACCOUNT = 'http://openapi.xg.qq.com/v2/application/del_app_account_all_tokens';

    // v3 接口
    const RESTAPI_PUSH = 'https://openapi.xg.qq.com/v3/push/app';

}

class TagTokenPair {

    public function __construct($tag, $token) {
        $this->tag = strval($tag);
        $this->token = strval($token);
    }

    public function __destruct() {
    }

    public $tag;
    public $token;
}

class Message {

    public function __construct() {
        $this->m_acceptTimes = array();
        $this->m_multiPkg = 0;
        $this->m_raw = "";
        $this->m_style = new Style(0);
        $this->m_action = new ClickAction();
    }

    public function __destruct() {
    }

    public function setTitle($title) {
        $this->m_title = $title;
    }

    public function setContent($content) {
        $this->m_content = $content;
    }

    public function setExpireTime($expireTime) {
        $this->m_expireTime = $expireTime;
    }

    public function getExpireTime() {
        return $this->m_expireTime;
    }

    public function setSendTime($sendTime) {
        $this->m_sendTime = $sendTime;
    }

    public function getSendTime() {
        return $this->m_sendTime;
    }

    public function addAcceptTime($acceptTime) {
        $this->m_acceptTimes[] = $acceptTime;
    }

    public function acceptTimeToJson() {
        $ret = array();
        foreach ($this->m_acceptTimes as $acceptTime) {
            $ret[] = $acceptTime->toArray();
        }
        return $ret;
    }

    public function setType($type) {
        $this->m_type = $type;
    }

    public function getType() {
        return $this->m_type;
    }

    public function setMultiPkg($multiPkg) {
        $this->m_multiPkg = $multiPkg;
    }

    public function getMultiPkg() {
        return $this->m_multiPkg;
    }

    public function setStyle($style) {
        $this->m_style = $style;
    }

    public function setAction($action) {
        $this->m_action = $action;
    }

    public function setCustom($custom) {
        $this->m_custom = $custom;
    }

    public function setRaw($raw) {
        $this->m_raw = $raw;
    }

    public function getLoopInterval() {
        return $this->m_loopInterval;
    }

    public function setLoopInterval($loopInterval) {
        $this->m_loopInterval = $loopInterval;
    }

    public function getLoopTimes() {
        return $this->m_loopTimes;
    }

    public function setLoopTimes($loopTimes) {
        $this->m_loopTimes = $loopTimes;
    }

    public function toJson() {
        if (!empty($this->m_raw)) {
            return $this->m_raw;
        }

        $ret = array();
        if ($this->m_type == self::TYPE_NOTIFICATION) {
            $ret['title'] = $this->m_title;
            $ret['content'] = $this->m_content;
            $ret['accept_time'] = $this->acceptTimeToJson();
            $ret_android = array();
            $ret_android['builder_id'] = $this->m_style->getBuilderId();
            $ret_android['ring'] = $this->m_style->getRing();
            $ret_android['vibrate'] = $this->m_style->getVibrate();
            $ret_android['clearable'] = $this->m_style->getClearable();
            $ret_android['n_id'] = $this->m_style->getNId();

            if (!is_null($this->m_style->getRingRaw())) {
                $ret_android['ring_raw'] = $this->m_style->getRingRaw();
            }
            $ret_android['lights'] = $this->m_style->getLights();
            $ret_android['icon_type'] = $this->m_style->getIconType();
            if (!is_null($this->m_style->getIconRes())) {
                $ret_android['icon_res'] = $this->m_style->getIconRes();
            }
            $ret_android['style_id'] = $this->m_style->getStyleId();
            if (!is_null($this->m_style->getSmallIcon())) {
                $ret_android['small_icon'] = $this->m_style->getSmallIcon();
            }

            $ret_android['action'] = $this->m_action->toJson();

            $ret['android'] = $ret_android;

        } else if ($this->m_type == self::TYPE_MESSAGE) {
            $ret['title'] = $this->m_title;
            $ret['content'] = $this->m_content;
            $ret['accept_time'] = $this->acceptTimeToJson();
        }
        $ret['android']['custom_content'] = $this->m_custom;
        return $ret;
    }

    public function isValid() {
        if (is_string($this->m_raw) && !empty($this->raw)) {
            return true;
        }

        if (!isset($this->m_title)) {
            $this->m_title = "";
        } else if (!is_string($this->m_title) || empty($this->m_title)) {
            return false;
        }

        if (!isset($this->m_content)) {
            $this->m_content = "";
        } else if (!is_string($this->m_content) || empty($this->m_content)) {
            return false;
        }

        if (!is_int($this->m_type) || $this->m_type < self::TYPE_NOTIFICATION || $this->m_type > self::TYPE_MESSAGE) {
            return false;
        }

        if (!is_int($this->m_multiPkg) || $this->m_multiPkg < 0 || $this->m_multiPkg > 1) {
            return false;
        }

        if ($this->m_type == self::TYPE_NOTIFICATION) {
            if (!($this->m_style instanceof Style) || !($this->m_action instanceof ClickAction)) {
                return false;
            }

            if (!$this->m_style->isValid() || !$this->m_action->isValid()) {
                return false;
            }

        }
        if (isset($this->m_expireTime)) {
            if (!is_int($this->m_expireTime) || $this->m_expireTime > 3 * 24 * 60 * 60) {
                return false;
            }

        } else {
            $this->m_expireTime = 0;
        }

        if (isset($this->m_sendTime)) {
            if (strtotime($this->m_sendTime) === false) {
                return false;
            }

        } else {
            $this->m_sendTime = "2013-12-19 17:49:00";
        }

        foreach ($this->m_acceptTimes as $value) {
            if (!($value instanceof TimeInterval) || !$value->isValid()) {
                return false;
            }

        }

        if (isset($this->m_custom)) {
            if (!is_array($this->m_custom)) {
                return false;
            }

        } else {
            $this->m_custom = array();
        }

        if (isset($this->m_loopInterval)) {
            if (!(is_int($this->m_loopInterval) && $this->m_loopInterval > 0)) {
                return false;
            }
        }

        if (isset($this->m_loopTimes)) {
            if (!(is_int($this->m_loopTimes) && $this->m_loopTimes > 0)) {
                return false;
            }
        }

        if (isset($this->m_loopInterval) && isset($this->m_loopTimes)) {
            if (($this->m_loopTimes - 1) * $this->m_loopInterval + 1 > self::MAX_LOOP_TASK_DAYS) {
                return false;
            }
        }

        return true;
    }

    private $m_title;
    private $m_content;
    private $m_expireTime;
    private $m_sendTime;
    private $m_acceptTimes;
    private $m_type;
    private $m_multiPkg;
    private $m_style;
    private $m_action;
    private $m_custom;
    private $m_raw;
    private $m_loopInterval;
    private $m_loopTimes;

    const TYPE_NOTIFICATION = 'notify';
    const TYPE_MESSAGE = 'message';
    const MAX_LOOP_TASK_DAYS = 15;
}

class MessageIOS {
    public function __construct() {
        $this->m_acceptTimes = array();
        $this->m_type = self::TYPE_APNS_NOTIFICATION;
    }

    public function __destruct() {
    }

    public function setTitle($title) {
        $this->m_title = $title;
    }

    public function setContent($content) {
        $this->m_content = $content;
    }

    public function setExpireTime($expireTime) {
        $this->m_expireTime = $expireTime;
    }

    public function getExpireTime() {
        return $this->m_expireTime;
    }

    public function setSendTime($sendTime) {
        $this->m_sendTime = $sendTime;
    }

    public function getSendTime() {
        return $this->m_sendTime;
    }

    public function addAcceptTime($acceptTime) {
        $this->m_acceptTimes[] = $acceptTime;
    }

    public function acceptTimeToJson() {
        $ret = array();
        foreach ($this->m_acceptTimes as $acceptTime) {
            $ret[] = $acceptTime->toArray();
        }
        return $ret;
    }

    public function setCustom($custom) {
        $this->m_custom = $custom;
    }

    public function setRaw($raw) {
        $this->m_raw = $raw;
    }

    public function setAlert($alert) {
        $this->m_alert = $alert;
    }

    public function setBadge($badge) {
        $this->m_badge = $badge;
    }

    public function setSound($sound) {
        $this->m_sound = $sound;
    }

    public function setType($type) {
        $this->m_type = $type;
    }

    public function getType() {
        return $this->m_type;
    }

    public function getCategory() {
        return $this->m_category;
    }

    public function setCategory($category) {
        $this->m_category = $category;
    }

    public function getLoopInterval() {
        return $this->m_loopInterval;
    }

    public function setLoopInterval($loopInterval) {
        $this->m_loopInterval = $loopInterval;
    }

    public function getLoopTimes() {
        return $this->m_loopTimes;
    }

    public function setLoopTimes($loopTimes) {
        $this->m_loopTimes = $loopTimes;
    }

    public function toJson() {
        if (!empty($this->m_raw)) {
            return $this->m_raw;
        }

        $ret = array();
        $ret['ios'] = $this->m_custom;
        $ret['accept_time'] = $this->acceptTimeToJson();

        $aps = array();
        if ($this->m_type == self::TYPE_APNS_NOTIFICATION) {
            $ret['title'] = $this->m_title;
            $ret['content'] = $this->m_content;
            $aps['alert'] = $this->m_alert;
            if (isset($this->m_badge)) {
                $aps['badge'] = $this->m_badge;
            }

            if (isset($this->m_sound)) {
                $aps['sound'] = $this->m_sound;
            }

            if (isset($this->m_category)) {
                $aps['category'] = $this->m_category;
            }

        } else if ($this->m_type == self::TYPE_REMOTE_NOTIFICATION) {
            $aps['content-available'] = 1;
        }
        $ret['ios']['aps'] = $aps;
        return $ret;
    }

    public function isValid() {
        if (isset($this->m_expireTime)) {
            if (!is_int($this->m_expireTime) || $this->m_expireTime > 3 * 24 * 60 * 60) {
                return false;
            }

        } else {
            $this->m_expireTime = 0;
        }

        if (isset($this->m_sendTime)) {
            if (strtotime($this->m_sendTime) === false) {
                return false;
            }

        } else {
            $this->m_sendTime = "2014-03-13 12:00:00";
        }

        if (!empty($this->m_raw)) {
            if (is_string($this->m_raw)) {
                return true;
            } else {
                return false;
            }

        }
        if (!is_int($this->m_type) || $this->m_type < self::TYPE_APNS_NOTIFICATION || $this->m_type > self::TYPE_REMOTE_NOTIFICATION) {
            return false;
        }

        foreach ($this->m_acceptTimes as $value) {
            if (!($value instanceof TimeInterval) || !$value->isValid()) {
                return false;
            }

        }

        if (isset($this->m_custom)) {
            if (!is_array($this->m_custom)) {
                return false;
            }

        } else {
            $this->m_custom = array();
        }
        if ($this->m_type == self::TYPE_APNS_NOTIFICATION) {
            if (!isset($this->m_alert)) {
                return false;
            }

            if (!is_string($this->m_alert) && !is_array($this->m_alert)) {
                return false;
            }

        }
        if (isset($this->m_badge)) {
            if (!is_int($this->m_badge)) {
                return false;
            }

        }
        if (isset($this->m_sound)) {
            if (!is_string($this->m_sound)) {
                return false;
            }

        }
        if (isset($this->m_loopInterval)) {
            if (!(is_int($this->m_loopInterval) && $this->m_loopInterval > 0)) {
                return false;
            }
        }
        if (isset($this->m_loopTimes)) {
            if (!(is_int($this->m_loopTimes) && $this->m_loopTimes > 0)) {
                return false;
            }
        }
        if (isset($this->m_loopInterval) && isset($this->m_loopTimes)) {
            if (($this->m_loopTimes - 1) * $this->m_loopInterval + 1 > self::MAX_LOOP_TASK_DAYS) {
                return false;
            }
        }

        return true;
    }

    private $m_title;
    private $m_content;
    private $m_expireTime;
    private $m_sendTime;
    private $m_acceptTimes;
    private $m_custom;
    private $m_raw;
    private $m_type;
    private $m_alert;
    private $m_badge;
    private $m_sound;
    private $m_category;
    private $m_loopInterval;
    private $m_loopTimes;

    const TYPE_APNS_NOTIFICATION = 'notify';
    const TYPE_REMOTE_NOTIFICATION = 'message';
    const MAX_LOOP_TASK_DAYS = 15;
}

class ClickAction {
    /**
     * 动作类型
     * @param int $actionType 1打开activity或app本身，2打开url，3打开Intent
     */
    public function __construct() {
        $this->m_atyAttrIntentFlag = 0;
        $this->m_atyAttrPendingIntentFlag = 0;
        $this->m_confirmOnPackageDownloadUrl = 1;
    }

    public function setActionType($actionType) {
        $this->m_actionType = $actionType;
    }

    public function setUrl($url) {
        $this->m_url = $url;
    }

    public function setComfirmOnUrl($comfirmOnUrl) {
        $this->m_confirmOnUrl = $comfirmOnUrl;
    }

    public function setActivity($activity) {
        $this->m_activity = $activity;
    }

    public function setIntent($intent) {
        $this->m_intent = $intent;
    }

    public function setAtyAttrIntentFlag($atyAttrIntentFlag) {
        $this->m_atyAttrIntentFlag = $atyAttrIntentFlag;
    }

    public function setAtyAttrPendingIntentFlag($atyAttrPendingIntentFlag) {
        $this->m_atyAttrPendingIntentFlag = $atyAttrPendingIntentFlag;
    }

    public function setPackageDownloadUrl($packageDownloadUrl) {
        $this->m_packageDownloadUrl = $packageDownloadUrl;
    }

    public function setConfirmOnPackageDownloadUrl($confirmOnPackageDownloadUrl) {
        $this->m_confirmOnPackageDownloadUrl = $confirmOnPackageDownloadUrl;
    }

    public function setPackageName($packageName) {
        $this->m_packageName = $packageName;
    }

    public function toJson() {
        $ret = array();
        $ret['action_type'] = $this->m_actionType;
        $ret['browser'] = array('url' => $this->m_url, 'confirm' => $this->m_confirmOnUrl);
        $ret['activity'] = $this->m_activity;
        $ret['intent'] = $this->m_intent;

        $aty_attr = array();
        if (isset($this->m_atyAttrIntentFlag)) {
            $aty_attr['if'] = $this->m_atyAttrIntentFlag;
        }
        if (isset($this->m_atyAttrPendingIntentFlag)) {
            $aty_attr['pf'] = $this->m_atyAttrPendingIntentFlag;
        }
        $ret['aty_attr'] = $aty_attr;

        return $ret;
    }

    public function isValid() {
        if (!isset($this->m_actionType)) {
            $this->m_actionType = self::TYPE_ACTIVITY;
        }

        if (!is_int($this->m_actionType)) {
            return false;
        }

        if ($this->m_actionType < self::TYPE_ACTIVITY || $this->m_actionType > self::TYPE_INTENT) {
            return false;
        }

        if ($this->m_actionType == self::TYPE_ACTIVITY) {
            if (!isset($this->m_activity)) {
                $this->m_activity = "";
                return true;
            }
            if (isset($this->m_atyAttrIntentFlag)) {
                if (!is_int($this->m_atyAttrIntentFlag)) {
                    return false;
                }
            }
            if (isset($this->m_atyAttrPendingIntentFlag)) {
                if (!is_int($this->m_atyAttrPendingIntentFlag)) {
                    return false;
                }
            }

            if (is_string($this->m_activity) && !empty($this->m_activity)) {
                return true;
            }

            return false;
        }

        if ($this->m_actionType == self::TYPE_URL) {
            if (is_string($this->m_url) && !empty($this->m_url) &&
                is_int($this->m_confirmOnUrl) &&
                $this->m_confirmOnUrl >= 0 && $this->m_confirmOnUrl <= 1
            ) {
                return true;
            }

            return false;
        }

        if ($this->m_actionType == self::TYPE_INTENT) {
            if (is_string($this->m_intent) && !empty($this->m_intent)) {
                return true;
            }

            return false;
        }
    }

    private $m_actionType;
    private $m_url;
    private $m_confirmOnUrl;
    private $m_activity;
    private $m_intent;
    private $m_atyAttrIntentFlag;
    private $m_atyAttrPendingIntentFlag;
    private $m_packageDownloadUrl;
    private $m_confirmOnPackageDownloadUrl;
    private $m_packageName;

    const TYPE_ACTIVITY = 1;
    const TYPE_URL = 2;
    const TYPE_INTENT = 3;
}

class Style {
    public function __construct($builderId, $ring = 0, $vibrate = 0, $clearable = 1, $nId = 0, $lights = 1, $iconType = 0, $styleId = 1) {
        $this->m_builderId = $builderId;
        $this->m_ring = $ring;
        $this->m_vibrate = $vibrate;
        $this->m_clearable = $clearable;
        $this->m_nId = $nId;
        $this->m_lights = $lights;
        $this->m_iconType = $iconType;
        $this->m_styleId = $styleId;
    }

    public function __destruct() {
    }

    public function getBuilderId() {
        return $this->m_builderId;
    }

    public function getRing() {
        return $this->m_ring;
    }

    public function getVibrate() {
        return $this->m_vibrate;
    }

    public function getClearable() {
        return $this->m_clearable;
    }

    public function getNId() {
        return $this->m_nId;
    }

    public function getLights() {
        return $this->m_lights;
    }

    public function getIconType() {
        return $this->m_iconType;
    }

    public function getStyleId() {
        return $this->m_styleId;
    }

    public function setRingRaw($ringRaw) {
        return $this->m_ringRaw = $ringRaw;
    }

    public function getRingRaw() {
        return $this->m_ringRaw;
    }

    public function setIconRes($iconRes) {
        return $this->m_iconRes = $iconRes;
    }

    public function getIconRes() {
        return $this->m_iconRes;
    }

    public function setSmallIcon($smallIcon) {
        return $this->m_smallIcon = $smallIcon;
    }

    public function getSmallIcon() {
        return $this->m_smallIcon;
    }

    public function isValid() {
        if (!is_int($this->m_builderId) || !is_int($this->m_ring) ||
            !is_int($this->m_vibrate) || !is_int($this->m_clearable) ||
            !is_int($this->m_lights) || !is_int($this->m_iconType) ||
            !is_int($this->m_styleId)
        ) {
            return false;
        }

        if ($this->m_ring < 0 || $this->m_ring > 1) {
            return false;
        }

        if ($this->m_vibrate < 0 || $this->m_vibrate > 1) {
            return false;
        }

        if ($this->m_clearable < 0 || $this->m_clearable > 1) {
            return false;
        }

        if ($this->m_lights < 0 || $this->m_lights > 1) {
            return false;
        }

        if ($this->m_iconType < 0 || $this->m_iconType > 1) {
            return false;
        }

        if ($this->m_styleId < 0 || $this->m_styleId > 1) {
            return false;
        }

        return true;
    }

    private $m_builderId;
    private $m_ring;
    private $m_vibrate;
    private $m_clearable;
    private $m_nId;
    private $m_ringRaw;
    private $m_lights;
    private $m_iconType;
    private $m_iconRes;
    private $m_styleId;
    private $m_smallIcon;
}

class TimeInterval {
    public function __construct($startHour, $startMin, $endHour, $endMin) {
        $this->m_startHour = $startHour;
        $this->m_startMin = $startMin;
        $this->m_endHour = $endHour;
        $this->m_endMin = $endMin;
    }

    public function __destruct() {
    }

    public function toArray() {
        return array(
            'start' => array('hour' => strval($this->m_startHour), 'min' => strval($this->m_startMin)),
            'end' => array('hour' => strval($this->m_endHour), 'min' => strval($this->m_endMin)),
        );
    }

    public function isValid() {
        if (!is_int($this->m_startHour) || !is_int($this->m_startMin) ||
            !is_int($this->m_endHour) || !is_int($this->m_endMin)
        ) {
            return false;
        }

        if ($this->m_startHour >= 0 && $this->m_startHour <= 23 &&
            $this->m_startMin >= 0 && $this->m_startMin <= 59 &&
            $this->m_endHour >= 0 && $this->m_endHour <= 23 &&
            $this->m_endMin >= 0 && $this->m_endMin <= 59
        ) {
            return true;
        } else {
            return false;
        }

    }

    private $m_startHour;
    private $m_startMin;
    private $m_endHour;
    private $m_endMin;
}

class ParamsBase {

    /**
     * @var array 当前传入的参数列表
     */
    public $_params = array();

    /**
     * 构造函数
     */
    public function __construct($params) {
        if (!is_array($params)) {
            return array();
        }
        foreach ($params as $key => $value) {
            //如果是非法的key值，则不使用这个key
            $this->_params[$key] = $value;
        }
    }

    public function set($k, $v) {
        if (!isset($k) || !isset($v)) {
            return;
        }
        $this->_params[$k] = $v;
    }

    /**
     * 根据实例化传入的参数生成签名
     */
    public function generateSign($method, $url, $secret_key) {
        //将参数进行升序排序
        $param_str = '';
        $method = strtoupper($method);
        $url_arr = parse_url($url);
        if (isset($url_arr['host']) && isset($url_arr['path'])) {
            $url = $url_arr['host'] . $url_arr['path'];
        }
        if (!empty($this->_params)) {
            ksort($this->_params);
            foreach ($this->_params as $key => $value) {
                $param_str .= $key . '=' . $value;
            }
        }
        //print $method.$url.$param_str.$secret_key."\n";
        return md5($method . $url . $param_str . $secret_key);
    }

}

class RequestBase {

    //get请求方式
    const METHOD_GET = 'get';
    //post请求方式
    const METHOD_POST = 'post';

    /**
     * 发起一个get或post请求
     * @param $url 请求的url
     * @param int $method 请求方式
     * @param array $params 请求参数
     * @param array $extra_conf curl配置, 高级需求可以用, 如
     * $extra_conf = array(
     *    CURLOPT_HEADER => true,
     *    CURLOPT_RETURNTRANSFER = false
     * )
     * @return bool|mixed 成功返回数据，失败返回false
     * @throws \Exception
     */
    public static function exec($url, $params = array(), $method = self::METHOD_GET, $extra_conf = array()) {
        //如果是get请求，直接将参数附在url后面
        if ($method == self::METHOD_GET) {
            $params = is_array($params) ? http_build_query($params) : $params;
            $url .= (strpos($url, '?') === false ? '?' : '&') . $params;
        }

        //默认配置
        $curl_conf = array(
            CURLOPT_URL => $url, //请求url
            CURLOPT_HEADER => false, //不输出头信息
            CURLOPT_RETURNTRANSFER => true, //不输出返回数据
            CURLOPT_CONNECTTIMEOUT => 3, // 连接超时时间
            CURLOPT_SSL_VERIFYPEER => false
        );

        //配置post请求额外需要的配置项
        if ($method == self::METHOD_POST) {
            $params = is_array($params) ? json_encode($params) : $params;
            //使用post方式
            $curl_conf[CURLOPT_POST] = true;
            //post参数
            $curl_conf[CURLOPT_POSTFIELDS] = $params;
            //数据类型和长度设置
            $curl_conf[CURLOPT_HTTPHEADER] = array(
                'Content-Type: application/json',
                'Content-Length: ' . strlen($params),
            );
        }
       // curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        //添加额外的配置
        foreach ($extra_conf as $k => $v) {
            $curl_conf[$k] = $v;
        }
        //var_dump($curl_conf);

        $data = false;
        try {
            //初始化一个curl句柄
            $curl_handle = curl_init();
            //设置curl的配置项
            curl_setopt_array($curl_handle, $curl_conf);
            //发起请求
            $data = curl_exec($curl_handle);
            if ($data === false) {
                throw new \Exception('CURL ERROR: ' . curl_error($curl_handle));
            }
        } catch (\Exception $e) {
            echo $e->getMessage();
        }
        curl_close($curl_handle);

        return $data;
    }

    public static function execForOld($url, $params = array(), $method = self::METHOD_GET, $extra_conf = array()) {
        $params = is_array($params) ? http_build_query($params) : $params;
        //如果是get请求，直接将参数附在url后面
        if ($method == self::METHOD_GET) {
            $url .= (strpos($url, '?') === false ? '?' : '&') . $params;
        }

        //默认配置
        $curl_conf = array(
            CURLOPT_URL => $url, //请求url
            CURLOPT_HEADER => false, //不输出头信息
            CURLOPT_RETURNTRANSFER => true, //不输出返回数据
            CURLOPT_CONNECTTIMEOUT => 3, // 连接超时时间
        );

        //配置post请求额外需要的配置项
        if ($method == self::METHOD_POST) {
            //使用post方式
            $curl_conf[CURLOPT_POST] = true;
            //post参数
            $curl_conf[CURLOPT_POSTFIELDS] = $params;
        }

        //添加额外的配置
        foreach ($extra_conf as $k => $v) {
            $curl_conf[$k] = $v;
        }
        //var_dump($curl_conf);

        $data = false;
        try {
            //初始化一个curl句柄
            $curl_handle = curl_init();
            //设置curl的配置项
            curl_setopt_array($curl_handle, $curl_conf);
            //发起请求
            $data = curl_exec($curl_handle);
            if ($data === false) {
                throw new \Exception('CURL ERROR: ' . curl_error($curl_handle));
            }
        } catch (\Exception $e) {
            echo $e->getMessage();
        }
        curl_close($curl_handle);

        return $data;
    }
}
